package com.book.filter;

import com.book.common.api.entity.CurrentUser;
import com.book.common.api.oauth.HystrixConstant;
import com.book.common.helper.I18nHelper;
import com.book.common.result.RestResponse;
import com.book.common.util.GuavaCacheUtil;
import com.book.common.util.RequestInfoUtil;
import com.book.configure.GateWayPropertiesConfig;
import com.book.oauth.server.user.hystrix.IOauthHystrix;
import com.book.upms.ip.dto.BlackIpDto;
import com.book.upms.ip.hystrix.IBlackIpHystrix;
import com.book.upms.ip.vo.BlackIpVo;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * Description：自定义全局Filter,过滤一定的无效路径，对某些需要鉴权的路径信息做判断，如果没有鉴权，重定向到指定的登录页面
 *
 * @Author： leo.xiong
 * @CreateDate： 2020/07/20 10:48
 * @Email： leo.xiong@suyun360.com
 * @Since：
 */
@Slf4j
@Component
public class CustomGateWayFilter implements GlobalFilter, Ordered {

    private static final String BLACK_IP_LIST = "blackIPList";

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Autowired
    private GateWayPropertiesConfig gateWayPropertiesConfig;

    @Autowired
    private IBlackIpHystrix blackIpHystrix;

    @Autowired
    private IOauthHystrix oauthHystrix;

    @Autowired
    private I18nHelper i18nHelper;

    @Autowired
    private ExecutorService customGateWayFilterExecutorService;

    /**
     * 1、判断当IP是否已经加入黑名单，是，直接重定向到某个路径
     * 2、判断当前路径是否是登录路径，是，直接放行
     * 3、判断当前路径是否是可以直接放行的路径，是，直接放行
     * 4、判断当前路径是否需要用户权限，无需用户权限直接放行
     * 5、获取token，获取用户信息，如果用户信息不存在，重定向到登录页面
     * 6、用户获取成功，放行
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            ServerHttpRequest serverHttpRequest = exchange.getRequest();
            String ip = RequestInfoUtil.getIp(serverHttpRequest.getRemoteAddress());
            if (!isBlackList(ip)) {
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                response.getHeaders().set(HttpHeaders.LOCATION, gateWayPropertiesConfig.getBlackPathRedirect());
                return response.setComplete();
            }
            String path = serverHttpRequest.getPath().toString();
            if (path.contains(gateWayPropertiesConfig.getLoginUrl())) {
                return chain.filter(exchange);
            }
            if (isExclusion(path)) {
                return chain.filter(exchange);
            }
            if (!isAuthPath(path)) {
                return chain.filter(exchange);
            }
            i18nHelper.setLocale(serverHttpRequest.getHeaders().getFirst(HystrixConstant.LOCAL_KEY), null);
            return chain.filter(exchange);
        } catch (Exception e) {
            log.error("GateWay Filter error:{}", e);
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            response.getHeaders().set(HttpHeaders.LOCATION, gateWayPropertiesConfig.getLoginUrl());
            return response.setComplete();
        } finally {
            i18nHelper.removeLocale();
        }
    }

    @Override
    public int getOrder() {
        return -100;
    }

    /**
     * 是否过滤
     *
     * @param path
     * @return
     */
    private Boolean isExclusion(String path) {
        String[] prefixExclusions = gateWayPropertiesConfig.getExclusion().split(",");
        for (String prefixExclusion : prefixExclusions) {
            if (pathMatcher.match(prefixExclusion, path)) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 是否需要用户权限的路径
     *
     * @param path
     * @return
     */
    private Boolean isAuthPath(String path) {
        String[] authPaths = gateWayPropertiesConfig.getAuthPath().split(",");
        for (String authPath : authPaths) {
            if (pathMatcher.match(authPath, path)) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 是否黑名单
     *
     * @param path
     * @return
     */
    private boolean isBlackList(String path) {
        Set<String> blackIpSet = GuavaCacheUtil.get(BLACK_IP_LIST, Set.class);
        if (blackIpSet == null) {
            customGateWayFilterExecutorService.execute(() -> {
                BlackIpDto blackIpDto = new BlackIpDto();
                RestResponse<List<BlackIpVo>> restResponse = blackIpHystrix.list(blackIpDto);
                Set<String> queryList = Sets.newHashSet();
                if (restResponse.isSuccess() && !CollectionUtils.isEmpty(restResponse.getData())) {
                    queryList = restResponse.getData().parallelStream().map(BlackIpVo::getBlackIp).collect(Collectors.toSet());
                }
                GuavaCacheUtil.put(BLACK_IP_LIST, queryList);
            });
            return true;
        }
        if (blackIpSet.contains(path)) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    private boolean verifyUser(ServerHttpRequest serverHttpRequest) {
        String token = serverHttpRequest.getHeaders().getFirst(HystrixConstant.TOKEN);
        if (StringUtils.isEmpty(token)) {
            log.info("token is empty");
            return Boolean.FALSE;
        }
        RestResponse<CurrentUser> currentUserRestResponse = oauthHystrix.getPrincipal(token);
        if (currentUserRestResponse.isFailed()) {
            log.info("token get currentUser is failed:{}", currentUserRestResponse.getErrorMsg(), currentUserRestResponse.getData());
            return Boolean.FALSE;
        }
        CurrentUser currentUser = currentUserRestResponse.getData();
        if (currentUser == null) {
            log.info("token get currentUser is failed:{}", currentUserRestResponse.getErrorMsg(), currentUserRestResponse.getData());
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}
